module queueOutSide (   
    input       wire              clk                       ,
    input       wire              rst                       ,
    input       wire              dict_update_en1           ,
    input       wire     [5:0]    dict_update_pri1          ,
    input       wire     [15:0]   dict_update_value1        ,
    input       wire              dict_update_en2           ,
    input       wire     [5:0]    dict_update_pri2          ,
    input       wire     [15:0]   dict_update_value2        ,
    output      reg               transmission_over1        ,
    output      reg               transmission_over2        ,
    output      reg      [15:0]   transmission_sram         ,
    output      reg      [15:0]   transmission_cram         ,

    input       wire     [15:0]   read_ready_sram           ,
    input       wire     [15:0]   read_ready_cram           ,
    input       wire              port_out_idle             ,       //输出端口是否空闲，空闲则可进行输出;port_out_idle初始化值应为1
    input       wire     [31:0]   hand_ack                  ,       //表示输出数据已经被正确接收的握手信号
    output      reg               dequeue_vld               ,       //出栈数据有效信号
    output      reg      [15:0]   dequeue_head_old          ,       //出栈老队头
    output      reg      [5:0]    old_head_priority                 //老队头优先级，用于仲裁
);
    //维护头尾指针
    reg     [15:0]      head       [7:0]    ;        //每个端口有8个虚拟的优先级队列，并且有一个count数组进行计数
    reg     [15:0]      tail       [7:0]    ;        //假设所有存储空间都存储64B的最短包，并且都往同一个端口的同一个优先级队列转发，
    reg     [13:0]      count      [7:0]    ;        //那么最长就会有32*256Kb/8/64B = 16384，即2的14次方
    reg                 last_port_out_idle  ;
    reg                 port_free           ;
    wire    [3:0]       strict_pri_order    ;
    wire    [15:0]      index               ;
    integer             i                   ;

    //----------------------------------------------------------------------------
    reg     [9:0]       queue_next        [511:0] ;   //存储包头的链接关系
    reg     [15:0]      packet_addr       [511:0] ;   //存储包头的寻址地址
    reg     [9:0]       available_list    [511:0] ;   //空闲块队列
    reg     [9:0]       available_head            ;   //空闲循环链表指针
    reg     [9:0]       available_head_next       ;   //空闲循环链表指针
    reg     [9:0]       available_tail            ;     
    reg     [1:0]       write_state               ;   //写状态机 00空闲、01问询状态、10入队成功信号
    //--------------------------------------------------------------------
    // 入队所需信息
    reg                 enqueue_en1               ;   //压栈使能
    reg     [5:0]       enqueue_priority1         ;   //数据包优先级
    reg     [9:0]       enqueue_value1            ;   //数据包地址（RAM块号6位+块内地址10位）
    reg                 enqueue_en2               ;   //压栈使能
    reg     [5:0]       enqueue_priority2         ;   //数据包优先级
    reg     [9:0]       enqueue_value2            ;   //数据包地址（RAM块号6位+块内地址10位）
    reg     [7:0]       enqueue_sucess            ;   //写入成功信号
    //----------------------------------------------------------------------------

    wire    [9:0]       write_index1              ;   //临时指针
    wire    [9:0]       write_index2              ;   //临时指针
    reg     [5:0]       expect_dequeue_ram        ;   //期望收到出队请求的RAM
    assign  write_index1 = available_list[available_head];
    assign  write_index2 = available_list[available_head_next];
    //----------------------------------------------------------------------------
    wire    [10:0]      all_sum                   ;
    assign  all_sum  =  count[0] + count[1] + count[2] + count[3] + count[4] + count[5] + count[6] + count[7];
    //----------------------------------------------------------------------------
    reg                 dequeue_vld1              ;      
    reg     [15:0]      dequeue_head_old1         ;
    reg     [5:0]       old_head_priority1        ;
    reg     [5:0]       expect_dequeue_ram1       ;
    reg                 dequeue_vld2              ;      
    reg     [15:0]      dequeue_head_old2         ;
    reg     [5:0]       old_head_priority2        ;
    reg     [5:0]       expect_dequeue_ram2       ;
    reg                 dequeue_vld3              ;      
    reg     [15:0]      dequeue_head_old3         ;
    reg     [5:0]       old_head_priority3        ;
    reg     [5:0]       expect_dequeue_ram3       ;
    reg     [1:0]       dq_sequence               ;
    //----------------------------------------------------------------------------
    wire                dq_en                     ;
    assign  dq_en   =   (!(dequeue_vld1 && dequeue_vld2 && dequeue_vld3) && all_sum > 'd0) ? 1 : 0;
    //----------------------------------------------------------------------------
    wire    [31:0]      read_ready;
    assign read_ready = {read_ready_cram, read_ready_sram};

    reg                 his_update_en1;
    reg                 his_update_en2;
    reg     [1:0]       out_state;
    reg                 w_state;

    assign strict_pri_order = count[7] > 0 ? 'd7 :
                              count[6] > 0 ? 'd6 :
                              count[5] > 0 ? 'd5 :
                              count[4] > 0 ? 'd4 :
                              count[3] > 0 ? 'd3 :
                              count[2] > 0 ? 'd2 :
                              count[1] > 0 ? 'd1 :
                              count[0] > 0 ? 'd0 :
                              'b1111;
    assign index = (strict_pri_order == 'd15) ? 'b1111_1111_1111_1111 : head[strict_pri_order];

    always @(posedge clk) begin
        if (rst) begin
            write_state = 2'b00;
            for (i=0; i<512; i=i+1) begin
                queue_next[i]     <= 0;
                packet_addr[i]    <= 0;
                available_list[i] <= i;
            end
            available_head <= 'd0;
            available_head_next <= 'd1;
            available_tail <= 'd0;
            enqueue_en1 <= 'b0;         
            enqueue_en2 <= 'b0;
            enqueue_priority1 <= 'd0;   
            enqueue_priority2 <= 'd0;
            enqueue_value1 <= 'd0;      
            enqueue_value2 <= 'd0;
            transmission_over1 <= 'd0;  
            transmission_over2 <= 'd0;
            transmission_sram <= 0;
            transmission_cram <= 0;
            his_update_en1 <= 'd0;
            his_update_en2 <= 'd0;
            for(i = 0; i < 8; i = i + 1) begin
                head[i]  <= 16'h0000;
                tail[i]  <= 16'h0000;
                count[i] <= 14'd0000;
                enqueue_sucess[i] <= 1'b0;
            end
            dequeue_head_old   <= 16'h0000;   
            old_head_priority  <= 'd0;
            w_state <= 'b0;
            
            port_free <= 1'b1;
            dequeue_vld <= 1'b0;
            dequeue_vld1 <= 'd0; 
            dequeue_head_old1 <= 'd0;
            old_head_priority1 <= 'd0;
            expect_dequeue_ram1 <= 'b111111;
            dequeue_vld2 <= 'd0; 
            dequeue_head_old2 <= 'd0;
            old_head_priority2 <= 'd0;
            expect_dequeue_ram2 <= 'b111111;
            dequeue_vld3 <= 'd0; 
            dequeue_head_old3 <= 'd0;
            old_head_priority3 <= 'd0;
            expect_dequeue_ram3 <= 'b111111;    
            dq_sequence <= 'd0;
            out_state <= 'b00;
        end
        else begin
            last_port_out_idle <= port_out_idle;    
            if (last_port_out_idle == 1'b0 && port_out_idle == 1'b1) begin  //输出端口前一个clk状�?�为忙，后一个状态为空闲时，表示可输�?
                port_free <= 1'b1;
            end
            if (out_state == 'b00) begin
                if (port_free == 1'b1 && (dequeue_vld1 || dequeue_vld2 || dequeue_vld3)) begin
                    if (read_ready[expect_dequeue_ram1] == 1'b1 && dequeue_vld1 == 1'b1) begin
                        dequeue_vld <= dequeue_vld1;
                        dequeue_head_old <= dequeue_head_old1;
                        expect_dequeue_ram <= expect_dequeue_ram1;
                        old_head_priority <= old_head_priority1;
                        dq_sequence <= 'd1;
                        out_state <= 'b01;
                    end     
                    else if (read_ready[expect_dequeue_ram2] == 1'b1 && dequeue_vld2 == 1'b1) begin
                        dequeue_vld <= dequeue_vld2;
                        dequeue_head_old <= dequeue_head_old2;
                        expect_dequeue_ram <= expect_dequeue_ram2;
                        old_head_priority <= old_head_priority2;
                        dq_sequence <= 'd2;
                        out_state <= 'b01;
                    end
                    else if (read_ready[expect_dequeue_ram3] == 1'b1 && dequeue_vld3 == 1'b1) begin
                        dequeue_vld <= dequeue_vld3;
                        dequeue_head_old <= dequeue_head_old3;
                        expect_dequeue_ram <= expect_dequeue_ram3;
                        old_head_priority <= old_head_priority3;
                        dq_sequence <= 'd3;
                        out_state <= 'b01;
                    end
                end
            end
            else if (out_state == 'b01) begin
                if (hand_ack[expect_dequeue_ram] == 1'b1) begin             //如果没收到数据被正确接收的ack，则输出数据�?直保�?
                    dequeue_vld <= 1'b0;
                    port_free <= 1'b0;
                    out_state <= 'b00;
                    if (dq_sequence == 'd1) begin
                        if (dequeue_vld3) begin                 //防止数据�?3被饿�?
                            dequeue_vld1 <= dequeue_vld3;
                            dequeue_head_old1 <= dequeue_head_old3;
                            old_head_priority1 <= old_head_priority3;
                            expect_dequeue_ram1 <= expect_dequeue_ram3;

                            dequeue_vld3 <= 'b0;
                            dequeue_head_old3 <= 'd0;
                            expect_dequeue_ram3 <= 'b111111;
                            old_head_priority3 <= 'd0;
                        end
                        else if (dequeue_vld2) begin
                            dequeue_vld1 <= dequeue_vld2;
                            dequeue_head_old1 <= dequeue_head_old2;
                            old_head_priority1 <= old_head_priority2;
                            expect_dequeue_ram1 <= expect_dequeue_ram2;

                            dequeue_vld2 <= 'b0;
                            dequeue_head_old2 <= 'd0;
                            expect_dequeue_ram2 <= 'b111111;
                            old_head_priority2 <= 'd0;
                        end
                        else begin
                            dequeue_vld1 <= 'b0;
                            dequeue_head_old1 <= 'd0;
                            expect_dequeue_ram1 <= 'b111111;
                            old_head_priority1 <= 'd0;
                        end
                    end
                    else if (dq_sequence == 'd2) begin
                        dequeue_vld2 <= 'b0;
                        dequeue_head_old2 <= 'd0;
                        expect_dequeue_ram2 <= 'b111111;
                        old_head_priority2 <= 'd0;
                    end
                    else if (dq_sequence == 'd3) begin
                        dequeue_vld3 <= 'b0;
                        dequeue_head_old3 <= 'd0;
                        expect_dequeue_ram3 <= 'b111111;
                        old_head_priority3 <= 'd0;
                    end
                end
                else if (read_ready[expect_dequeue_ram] == 1'b0) begin
                    out_state <= 'b00;
                    dequeue_vld <= 1'b0;
                end
            end
            if (write_state == 2'b00) begin
                if (dq_en) begin       // 填充3个输出缓冲区
                    if (dequeue_vld1 == 'd0) begin
                        dequeue_vld1 <= 'd1;
                        dequeue_head_old1 <= packet_addr[index];
                        old_head_priority1 <= strict_pri_order;
                        
                        expect_dequeue_ram1 <= packet_addr[index][15:10];
                    end
                    else if (dequeue_vld2 == 'd0) begin
                        dequeue_vld2 <= 'd1;
                        dequeue_head_old2 <= packet_addr[index];
                        old_head_priority2 <= strict_pri_order;

                        expect_dequeue_ram2 <= packet_addr[index][15:10];
                    end
                    else if (dequeue_vld3 == 'd0) begin
                        dequeue_vld3 <= 'd1;
                        dequeue_head_old3 <= packet_addr[index];
                        old_head_priority3 <= strict_pri_order;

                        expect_dequeue_ram3 <= packet_addr[index][15:10];
                    end

                    packet_addr[index] <= 'd0;
                    queue_next[index] <= 'd0;

                    if (count[strict_pri_order] == 'd1) begin       //队列中最后一个元素，则无�?返回新的队头或队�?
                        head[strict_pri_order] <= 'd0;
                        tail[strict_pri_order] <= 'd0;
                    end
                    else begin
                        head[strict_pri_order] <= queue_next[index];
                    end
                    count[strict_pri_order] <= count[strict_pri_order] - 1'b1;

                    available_list[available_tail] <= index;
                    if (available_tail < 'd511) begin                 //空闲队列指针向后移动并取模，实现循环队列的功�?
                        available_tail <= available_tail + 'd1;
                    end
                    else begin
                        available_tail <= 'd0;
                    end
                end
                else begin
                    for(i = 0; i < 8; i = i + 1) begin
                        enqueue_sucess[i] <= 1'b0;
                    end
                    if ((dict_update_en1 || dict_update_en2) && !dq_en && all_sum <= 510) begin     //空闲状态并收到写使能信号
                        write_state <= 2'b01;
                        his_update_en1 <= dict_update_en1;
                        his_update_en2 <= dict_update_en2;
                        if (dict_update_en1 && dict_update_en2) begin
                            enqueue_en1       <= 1'b1;
                            enqueue_priority1 <= dict_update_pri1;
                            enqueue_value1    <= write_index1;
                            enqueue_en2       <= 1'b1;
                            enqueue_priority2 <= dict_update_pri2;
                            enqueue_value2    <= write_index2;
                            packet_addr[write_index1] <= dict_update_value1;
                            packet_addr[write_index2] <= dict_update_value2;
                            if (dict_update_pri1 != dict_update_pri2) begin
                                if (count[dict_update_pri1] != 0) begin
                                    queue_next[tail[dict_update_pri1]] <= write_index1;
                                end
                                if (count[dict_update_pri2] != 0) begin
                                    queue_next[tail[dict_update_pri2]] <= write_index2;
                                end
                            end
                            else begin
                                queue_next[tail[dict_update_pri1]] <= write_index1;
                                queue_next[write_index1] <= write_index2;
                            end
                            
                            if (available_head < 'd510) begin                 //空闲队列指针向后移动并取模，实现循环队列的功能
                                available_head <= available_head + 'd2;
                            end
                            else if (available_head == 'd510) begin
                                available_head <= 'd0;
                            end
                            else if (available_head == 'd511) begin
                                available_head <= 'd1;
                            end
                            
                            if (available_head_next < 'd510) begin                 //空闲队列指针向后移动并取模，实现循环队列的功能
                                available_head_next <= available_head_next + 2;
                            end
                            else if (available_head_next == 'd510) begin
                                available_head_next <= 'd0;
                            end
                            else if (available_head_next == 'd511) begin
                                available_head_next <= 'd1;
                            end
                        end
                        else if (dict_update_en1) begin
                            enqueue_en1       <= 1'b1;
                            enqueue_priority1 <= dict_update_pri1;
                            enqueue_value1    <= write_index1;

                            packet_addr[write_index1] <= dict_update_value1;
                            if (count[dict_update_pri1] != 0) begin
                                queue_next[tail[dict_update_pri1]] <= write_index1;
                            end

                            if (available_head < 'd511) begin                 //空闲队列指针向后移动并取模，实现循环队列的功能
                                available_head <= available_head + 'd1;
                            end
                            else begin
                                available_head <= 'd0;
                            end
                            if (available_head_next < 'd511) begin                 //空闲队列指针向后移动并取模，实现循环队列的功能
                                available_head_next <= available_head_next + 'd1;
                            end
                            else begin
                                available_head_next <= 'd0;
                            end
                        end
                        else if(dict_update_en2) begin
                            enqueue_en2       <= 1'b1;
                            enqueue_priority2 <= dict_update_pri2;
                            enqueue_value2    <= write_index1;

                            packet_addr[write_index1] <= dict_update_value2;
                            if (count[dict_update_pri2] != 0) begin
                                queue_next[tail[dict_update_pri2]] <= write_index1;
                            end
                            
                            if (available_head < 'd511) begin                 //空闲队列指针向后移动并取模，实现循环队列的功能
                                available_head <= available_head + 'd1;
                            end
                            else begin
                                available_head <= 'd0;
                            end
                            if (available_head_next < 'd511) begin                 //空闲队列指针向后移动并取模，实现循环队列的功能
                                available_head_next <= available_head_next + 'd1;
                            end
                            else begin
                                available_head_next <= 'd0;
                            end
                        end
                    end
                    else begin
                        enqueue_en1 <= 'b0;
                        enqueue_en2 <= 'b0;
                    end
                end

            end
            else if (write_state == 2'b01) begin
                if (enqueue_en1 && enqueue_en2) begin
                    if (w_state == 1'b0) begin
                        w_state <= 1'b1;
                        if (count[enqueue_priority1] == 'd0) begin           
                            head[enqueue_priority1] <= enqueue_value1;      
                        end
                        tail[enqueue_priority1] <= enqueue_value1;
                        count[enqueue_priority1] <= count[enqueue_priority1] + 'd1;
                        enqueue_sucess[enqueue_priority1] <= 1'b1;
                    end
                    else if (w_state == 1'b1) begin
                        w_state <= 1'b0;
                        if (count[enqueue_priority2] == 'd0) begin           
                            head[enqueue_priority2] <= enqueue_value2;      
                        end
                        tail[enqueue_priority2] <= enqueue_value2;
                        count[enqueue_priority2] <= count[enqueue_priority2] + 'd1;
                        enqueue_sucess[enqueue_priority2] <= 1'b1;
                        write_state <= 2'b10;
                    end
                end
                else if (enqueue_en1) begin
                    if (count[enqueue_priority1] == 'd0) begin           
                        head[enqueue_priority1] <= enqueue_value1;      
                    end
                    tail[enqueue_priority1] <= enqueue_value1;
                    count[enqueue_priority1] <= count[enqueue_priority1] + 'd1;
                    enqueue_sucess[enqueue_priority1] <= 1'b1;
                    write_state <= 2'b10;   
                end
                else if (enqueue_en2) begin
                    if (count[enqueue_priority2] == 'd0) begin           
                        head[enqueue_priority2] <= enqueue_value2;      
                    end
                    tail[enqueue_priority2] <= enqueue_value2;
                    count[enqueue_priority2] <= count[enqueue_priority2] + 'd1;
                    enqueue_sucess[enqueue_priority2] <= 1'b1;
                    write_state <= 2'b10;
                end
            end
            else if (write_state == 2'b10) begin    //修改状态，并收到入队成功信号
                if (his_update_en1 && his_update_en2) begin
                    if (enqueue_sucess[dict_update_pri1] && enqueue_sucess[dict_update_pri2]) begin
                        transmission_over1 <= 'd1;
                        transmission_over2 <= 'd1;
                        transmission_cram <= (1 << (dict_update_value1[15:10]-16));
                        transmission_sram <= (1 << dict_update_value2[15:10]);
                    end
                end
                else if (his_update_en1) begin
                    if (enqueue_sucess[dict_update_pri1]) begin
                        transmission_over1 <= 'd1;
                        transmission_cram <= (1 << (dict_update_value1[15:10]-16));
                    end
                end
                else if (his_update_en2) begin
                    if (enqueue_sucess[dict_update_pri2]) begin
                        transmission_over2 <= 'd1;
                        transmission_sram <= (1 << dict_update_value2[15:10]);
                    end
                end

                if (transmission_over1) begin
                    write_state <= 2'b00;
                    transmission_over1 <= 'd0;
                    transmission_cram <= 0;
                    his_update_en1 <= 1'b0;
                end

                if (transmission_over2) begin
                    write_state <= 2'b00;
                    transmission_over2 <= 'd0;
                    transmission_sram <= 0;
                    his_update_en2 <= 1'b0;
                end
            end
        end
    end
endmodule

